import 'dart:convert';
import 'dart:isolate';

import 'package:im_app/im/uploadAndDownload/FileUploadSocket.dart';
import 'package:im_app/im/util/ImSingleton.dart';
import 'package:logger/logger.dart';

import '../dao/ImMessageDao.dart';
import '../pojo/ImPojo.dart';
import '../socket/dto/base/Resp.dart';
import '../socket/dto/business/SendData.dart';
import '../socket/service/RpcSendDataService.dart';
import '../util/ImConstant.dart';
import '../util/MessageUtil.dart';
import '../util/UiUtil.dart';
import 'FileDownloadService.dart';
import 'ImSocket.dart';
import 'UploadAndDownloadDto.dart';

const String TAG = 'FileUploadService';

class FileUploadService {

  final _logger = Logger(printer: PrettyPrinter(),);

  Future<void> sendFile(ImMessage message, UploadCallback callback) async {
    if (!message.messageType.isFile()) return;

    message.fileStatus = ImMessageFileStatus.uploading;
    UiUtil.uiUpdateMessage(message);

    // 开启线程
    _FileUploadWork uploadWork = _FileUploadWork();
    try {
      await uploadWork.sendFile(message, wrapUploadCallback(uploadWork, callback, message));
    } catch(error, stackTrace) {
      _logger.e('Upload upload error', error: error, stackTrace: stackTrace);
    }
  }

  void _uploadSuccess(ImMessage message) async {
    _logger.i("[Upload success]");
    var imMessageDao = ImMessageDao();
    await imMessageDao.changeMessageFileStatus(message.id!, ImMessageFileStatus.uploaded);

    await saveFileToDb(message);

    message.fileStatus = ImMessageFileStatus.uploaded;
    message.updateTime = DateTime.now();
    UiUtil.uiUpdateMessage(message);

    var fileUploadSuccessReq = FileUploadSuccessReq(messageId: message.serverId!);

    var rpcMessageService = RpcMessageService();
    await rpcMessageService.sendFileUploadSuccess(fileUploadSuccessReq);
    _logger.i("$TAG, [Upload success send to server success], 文件上传成功消息发送至服务端");
  }

  UploadCallback wrapUploadCallback(_FileUploadWork uploadWork, UploadCallback callback, ImMessage message) {
    return (FileUploadResult result) {
      _logger.i('$TAG, [Upload call back], ${result.fileUploadSize}/${result.fileSize}/${result.errorMsg}');
      if (result.isSuccess) {
        _uploadSuccess(message);
        uploadWork.close();
      }
      callback(result);
    };
  }
}

class _FileUploadWork {

  final _logger = Logger(printer: PrettyPrinter(),);
  
  late Isolate isolate;
  late SendPort isolateSendPort;

  _FileUploadWork() {}

  sendFile(ImMessage message, UploadCallback callback) async {
    _logger.i('Upload sendFile started');

    ReceivePort receivePort = ReceivePort();
    isolate = await Isolate.spawn(_doUploadWorker, receivePort.sendPort);
    _logger.i('Upload worker created, ${isolate.debugName}');

    receivePort.listen((dynamic data){
      _logger.i('Upload receive data from worker');
      if (data is FileUploadResult) {
        FileUploadResult result = data;
        callback(result);
        if (result.isSuccess) {
          close();
        }
      } else if (data is SendPort) {
        isolateSendPort = data as SendPort;

        sendUploadDataToWorker(message);
      } else{
        _logger.e('Upload work receive data');
      };
    });
  }

  // 发送数据至UploadWorker线程
  void sendUploadDataToWorker(ImMessage message) async {
    try {
      var originName = message.messageExt?.originalName??message.content;

      var filePath = await MessageUtil.getFilePath(message);

      var ip = ImSingleton.getInstance().session.getData("im_ip") as String;
      var tenantCode = ImConstant.TENANT_CODE;

      var data = FileUploadData(tenantCode!, message.currentUserId, message.id!, message.serverId!,
          originName!, filePath, ip, 9190);

      isolateSendPort.send(data);
      _logger.i('Upload send file info to worker');
    } catch (error, stackTrace) {
      _logger.e('Upload send file info to worker error', error: error, stackTrace: stackTrace);
    }
  }

  void close() {
    _logger.i('Upload worker close!');
    isolate?.kill();
  }
}

final _logger = Logger(printer: PrettyPrinter(),);

void _doUploadWorker(SendPort sendPort) {
  _logger.i("UploadWorker start!!!!!!!");
  ReceivePort receivePort = ReceivePort();
  sendPort.send(receivePort.sendPort);

  receivePort.listen((dynamic message) async {
    _logger.i("UploadWorker receive request:  ${message}");

    var fileUploadData = message as FileUploadData;
    var fileUploadSocket = FileUploadSocket(fileUploadData.tenantCode, fileUploadData.userId, fileUploadData.messageId,
        fileUploadData.messageServerId, fileUploadData.fileOriginName, fileUploadData.filePath, fileUploadData.ip,
        fileUploadData.port);

    fileUploadSocket.setUploadListener((FileUploadResult fileUploadResult)=>{
      fileUploadResultCallBack(sendPort, fileUploadSocket, fileUploadResult)
    });

    try {
      await fileUploadSocket.connect();
      var response = await fileUploadSocket.sendFileInfoReq(60);
      _logger.i("$TAG, [Upload send file req result], ${jsonEncode(response)}");
      if(response is ExceptionResp) {
        sendPort.send(FileUploadResult(0, 0, false, (response as ExceptionResp).errorMsg??''));
        return;
      }
      if(response is OkResp) {
        _logger.i("$TAG, [Upload send file req result success], ${jsonEncode(response)}");
      }
      await fileUploadSocket.sendFile();
    } catch (error, stackTrace) {
      _logger.e("$TAG error", error: error, stackTrace: stackTrace);
      sendPort.send(FileUploadResult(0, 0, false, error.toString()));
      closeSocket(fileUploadSocket);
    }
  });
}

void fileUploadResultCallBack(SendPort sendPort, ImSocket imSocket, FileUploadResult fileUploadResult) async {
  sendPort.send(fileUploadResult);
  if (fileUploadResult.errorMsg != null && fileUploadResult.errorMsg != '') { // 出现异常
    await closeSocket(imSocket);
  } else if (fileUploadResult.isSuccess) { // 文件上传成功
    await closeSocket(imSocket);
  }
}

Future<void> closeSocket(ImSocket imSocket) async {
  await imSocket.close();
  _logger.i("$TAG, [Upload socket close]");
}
